<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  
  <meta http-equiv="X-UA-Compatible" content="IE=edge" >
  <title>iOS中保证线程安全的几种方式与性能对比 | Grx Blog</title>
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
  <meta name="description" content="iOS中保证线程安全的几种方式与性能对比一、前言 前段时间看了几个开源项目，发现他们保持线程同步的方式各不相同，有@synchronized、NSLock、dispatch_semaphore、NSCondition、pthread_mutex、OSSpinLock。后来网上查了一下，发现他们的实现机制各不相同，性能也各不一样。不好意思，我们平常使用最多的@synchronized是性能最差的。下">
<meta property="og:type" content="article">
<meta property="og:title" content="iOS中保证线程安全的几种方式与性能对比">
<meta property="og:url" content="http://yoursite.com/2016/05/07/iOS中保证线程安全的几种方式与性能对比/index.html">
<meta property="og:site_name" content="Grx Blog">
<meta property="og:description" content="iOS中保证线程安全的几种方式与性能对比一、前言 前段时间看了几个开源项目，发现他们保持线程同步的方式各不相同，有@synchronized、NSLock、dispatch_semaphore、NSCondition、pthread_mutex、OSSpinLock。后来网上查了一下，发现他们的实现机制各不相同，性能也各不一样。不好意思，我们平常使用最多的@synchronized是性能最差的。下">
<meta property="og:updated_time" content="2016-07-07T02:11:20.000Z">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="iOS中保证线程安全的几种方式与性能对比">
<meta name="twitter:description" content="iOS中保证线程安全的几种方式与性能对比一、前言 前段时间看了几个开源项目，发现他们保持线程同步的方式各不相同，有@synchronized、NSLock、dispatch_semaphore、NSCondition、pthread_mutex、OSSpinLock。后来网上查了一下，发现他们的实现机制各不相同，性能也各不一样。不好意思，我们平常使用最多的@synchronized是性能最差的。下">
  
    <link rel="alternative" href="/atom.xml" title="Grx Blog" type="application/atom+xml">
  
  
    <link rel="icon" href="/favicon.png">
  
  <link rel="stylesheet" href="/css/style.css">
</head>

<body>
  <div id="container">
    <div class="left-col">
    <div class="overlay"></div>
<div class="intrude-less">
	<header id="header" class="inner">
		<a href="/" class="profilepic">
			
			<img lazy-src="/img/rufei.jpg" class="js-avatar">
			
		</a>

		<hgroup>
		  <h1 class="header-author"><a href="/">Grx</a></h1>
		</hgroup>

		
		<p class="header-subtitle">自我管理，知识管理，时间管理，阅读，语音写作，思维导图</p>
		

		
			<div class="switch-btn">
				<div class="icon">
					<div class="icon-ctn">
						<div class="icon-wrap icon-house" data-idx="0">
							<div class="birdhouse"></div>
							<div class="birdhouse_holes"></div>
						</div>
						<div class="icon-wrap icon-ribbon hide" data-idx="1">
							<div class="ribbon"></div>
						</div>
						
						<div class="icon-wrap icon-link hide" data-idx="2">
							<div class="loopback_l"></div>
							<div class="loopback_r"></div>
						</div>
						
						
						<div class="icon-wrap icon-me hide" data-idx="3">
							<div class="user"></div>
							<div class="shoulder"></div>
						</div>
						
					</div>
					
				</div>
				<div class="tips-box hide">
					<div class="tips-arrow"></div>
					<ul class="tips-inner">
						<li>菜单</li>
						<li>标签</li>
						
						<li>友情链接</li>
						
						
						<li>关于我</li>
						
					</ul>
				</div>
			</div>
		

		<div class="switch-area">
			<div class="switch-wrap">
				<section class="switch-part switch-part1">
					<nav class="header-menu">
						<ul>
						
							<li><a href="/">主页</a></li>
				        
							<li><a href="/tags/life">随笔</a></li>
				        
							<li><a href="/archives">所有文章</a></li>
				        
						</ul>
					</nav>
					<nav class="header-nav">
						<div class="social">
							
								<a class="github" target="_blank" href="https://github.com/ruixiaoguo" title="github">github</a>
					        
								<a class="weibo" target="_blank" href="https://weibo.com/u/3984935859?refer_flag=1005055010_" title="weibo">weibo</a>
					        
								<a class="rss" target="_blank" href="#" title="rss">rss</a>
					        
								<a class="zhihu" target="_blank" href="#" title="zhihu">zhihu</a>
					        
						</div>
					</nav>
				</section>
				
				
				<section class="switch-part switch-part2">
					<div class="widget tagcloud" id="js-tagcloud">
						<a href="/tags/OC/" style="font-size: 10px;">-OC</a> <a href="/tags/Android/" style="font-size: 10px;">Android</a> <a href="/tags/Apple-Watch/" style="font-size: 10px;">Apple Watch</a> <a href="/tags/Flutter/" style="font-size: 13.33px;">Flutter</a> <a href="/tags/Life/" style="font-size: 10px;">Life</a> <a href="/tags/Lottie/" style="font-size: 10px;">Lottie</a> <a href="/tags/MMKV/" style="font-size: 10px;">MMKV</a> <a href="/tags/MonkeyKing/" style="font-size: 10px;">MonkeyKing</a> <a href="/tags/OC/" style="font-size: 20px;">OC</a> <a href="/tags/RAC/" style="font-size: 11.67px;">RAC</a> <a href="/tags/Swift/" style="font-size: 15px;">Swift</a> <a href="/tags/WKWebView/" style="font-size: 10px;">WKWebView</a> <a href="/tags/WebView/" style="font-size: 10px;">WebView</a> <a href="/tags/Xcode/" style="font-size: 18.33px;">Xcode</a> <a href="/tags/appledoc/" style="font-size: 10px;">appledoc</a> <a href="/tags/hexo/" style="font-size: 11.67px;">hexo</a> <a href="/tags/ios/" style="font-size: 10px;">ios</a> <a href="/tags/life/" style="font-size: 16.67px;">life</a> <a href="/tags/xcode/" style="font-size: 10px;">xcode</a>
					</div>
				</section>
				
				
				
				<section class="switch-part switch-part3">
					<div id="js-friends">
					
			          <a target="_blank" class="main-nav-link switch-friends-link" href="http://blog.devtang.com">唐巧的博客</a>
			        
			          <a target="_blank" class="main-nav-link switch-friends-link" href="http://beyondvincent.com">破船之家</a>
			        
			          <a target="_blank" class="main-nav-link switch-friends-link" href="http://limboy.me">无网不剩</a>
			        
			          <a target="_blank" class="main-nav-link switch-friends-link" href="http://msching.github.io">码农人生</a>
			        
			          <a target="_blank" class="main-nav-link switch-friends-link" href="http://yulingtianxia.com">玉令天下</a>
			        
			          <a target="_blank" class="main-nav-link switch-friends-link" href="http://blog.leichunfeng.com">雷纯锋的技术博客</a>
			        
			          <a target="_blank" class="main-nav-link switch-friends-link" href="http://jax.cnblogs.com">包建强</a>
			        
			          <a target="_blank" class="main-nav-link switch-friends-link" href="http://blog.cnbang.net">bang</a>
			        
			        </div>
				</section>
				

				
				
				<section class="switch-part switch-part4">
				
					<div id="js-aboutme">知识管理，时间管理，自我管理，阅读，语音写作，思维导图，Wiz，TimeMeter</div>
				</section>
				
			</div>
		</div>
	</header>				
</div>

    </div>
    <div class="mid-col">
      <nav id="mobile-nav">
  	<div class="overlay">
  		<div class="slider-trigger"></div>
  		<h1 class="header-author js-mobile-header hide">Grx</h1>
  	</div>
	<div class="intrude-less">
		<header id="header" class="inner">
			<div class="profilepic">
			
				<img lazy-src="/img/rufei.jpg" class="js-avatar">
			
			</div>
			<hgroup>
			  <h1 class="header-author">Grx</h1>
			</hgroup>
			
			<p class="header-subtitle">自我管理，知识管理，时间管理，阅读，语音写作，思维导图</p>
			
			<nav class="header-menu">
				<ul>
				
					<li><a href="/">主页</a></li>
		        
					<li><a href="/tags/life">随笔</a></li>
		        
					<li><a href="/archives">所有文章</a></li>
		        
		        <div class="clearfix"></div>
				</ul>
			</nav>
			<nav class="header-nav">
				<div class="social">
					
						<a class="github" target="_blank" href="https://github.com/ruixiaoguo" title="github">github</a>
			        
						<a class="weibo" target="_blank" href="https://weibo.com/u/3984935859?refer_flag=1005055010_" title="weibo">weibo</a>
			        
						<a class="rss" target="_blank" href="#" title="rss">rss</a>
			        
						<a class="zhihu" target="_blank" href="#" title="zhihu">zhihu</a>
			        
				</div>
			</nav>
		</header>				
	</div>
</nav>

      <div class="body-wrap"><article id="post-iOS中保证线程安全的几种方式与性能对比" class="article article-type-post" itemscope itemprop="blogPost">
  
    <div class="article-meta">
      <a href="/2016/05/07/iOS中保证线程安全的几种方式与性能对比/" class="article-date">
  	<time datetime="2016-05-07T01:45:59.000Z" itemprop="datePublished">2016-05-07</time>
</a>
    </div>
  
  <div class="article-inner">
    
      <input type="hidden" class="isFancy" />
    
    
      <header class="article-header">
        
  
    <h1 class="article-title" itemprop="name">
      iOS中保证线程安全的几种方式与性能对比
    </h1>
  

      </header>
      
      <div class="article-info article-info-post">
        
	<div class="article-tag tagcloud">
		<ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/OC/">-OC</a></li></ul>
	</div>

        

        <div class="clearfix"></div>
      </div>
      
    
    <div class="article-entry" itemprop="articleBody">
      
        <h2 id="iOS中保证线程安全的几种方式与性能对比"><a href="#iOS中保证线程安全的几种方式与性能对比" class="headerlink" title="iOS中保证线程安全的几种方式与性能对比"></a>iOS中保证线程安全的几种方式与性能对比</h2><h3 id="一、前言"><a href="#一、前言" class="headerlink" title="一、前言"></a>一、前言</h3><p><br><br> 前段时间看了几个开源项目，发现他们保持线程同步的方式各不相同，有@synchronized、NSLock、dispatch_semaphore、NSCondition、pthread_mutex、OSSpinLock。后来网上查了一下，发现他们的实现机制各不相同，性能也各不一样。不好意思，我们平常使用最多的@synchronized是性能最差的。下面我们先分别介绍每个加锁方式的使用，在使用一个案例来对他们进行性能对比</p>
<a id="more"></a>
<h3 id="二、介绍与使用"><a href="#二、介绍与使用" class="headerlink" title="二、介绍与使用"></a>二、介绍与使用</h3><p><br></p>
<h4 id="2-1、-synchronized"><a href="#2-1、-synchronized" class="headerlink" title="2.1、@synchronized"></a>2.1、@synchronized</h4><pre><code>    NSObject *obj = [[NSObject alloc] init];
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,  0), ^{
    @synchronized(obj) {
    NSLog(@&quot;需要线程同步的操作1 开始&quot;);
    sleep(3);
    NSLog(@&quot;需要线程同步的操作1 结束&quot;);
}
});
   dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
   sleep(1);
   @synchronized(obj) {
   NSLog(@&quot;需要线程同步的操作2&quot;);
}
});
</code></pre><p>@synchronized(obj)指令使用的obj为该锁的唯一标识，只有当标识相同时，才为满足互斥，如果线程2中的@synchronized(obj)改为@synchronized(self),刚线程2就不会被阻塞，@synchronized指令实现锁的优点就是我们不需要在代码中显式的创建锁对象，便可以实现锁的机制，但作为一种预防措施，@synchronized块会隐式的添加一个异常处理例程来保护代码，该处理例程会在异常抛出的时候自动的释放互斥锁。所以如果不想让隐式的异常处理例程带来额外的开销，你可以考虑使用锁对象。</p>
<p>上面结果的执行结果为：</p>
<pre><code>2016-06-29 20:48:35.747 SafeMultiThread[35945:580107] 需要线程同步的操作1 开始
2016-06-29 20:48:38.748 SafeMultiThread[35945:580107] 需要线程同步的操作1 结束
2016-06-29 20:48:38.749 SafeMultiThread[35945:580118] 需要线程同步的操作2
</code></pre><h4 id="2-2、dispatch-semaphore"><a href="#2-2、dispatch-semaphore" class="headerlink" title="2.2、dispatch_semaphore"></a>2.2、dispatch_semaphore</h4><pre><code> dispatch_semaphore_t signal = dispatch_semaphore_create(1);
 dispatch_time_t overTime = dispatch_time(DISPATCH_TIME_NOW, 3 * NSEC_PER_SEC);
 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
 dispatch_semaphore_wait(signal, overTime);
      NSLog(@&quot;需要线程同步的操作1 开始&quot;);
      sleep(2);
      NSLog(@&quot;需要线程同步的操作1 结束&quot;);
      dispatch_semaphore_signal(signal);
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  sleep(1);
  dispatch_semaphore_wait(signal, overTime);
      NSLog(@&quot;需要线程同步的操作2&quot;);
  dispatch_semaphore_signal(signal);
});
</code></pre><p>dispatch_semaphore是GCD用来同步的一种方式，与他相关的共有三个函数，分别是dispatch_semaphore_create，dispatch_semaphore_signal，dispatch_semaphore_wait。</p>
<p>（1）dispatch_semaphore_create的声明为：</p>
<pre><code>dispatch_semaphore_t dispatch_semaphore_create(long value);
</code></pre><p> 传入的参数为long，输出一个dispatch_semaphore_t类型且值为value的信号量。</p>
<p> 值得注意的是，这里的传入的参数value必须大于或等于0，否则dispatch_semaphore_create会返回NULL。</p>
<p> （2）dispatch_semaphore_signal的声明为：</p>
<pre><code>long dispatch_semaphore_signal(dispatch_semaphore_t dsema);
</code></pre><p>这个函数会使传入的信号量dsema的值加1；</p>
<p> (3) dispatch_semaphore_wait的声明为：</p>
<pre><code>long dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout);
</code></pre><p>这个函数会使传入的信号量dsema的值减1；这个函数的作用是这样的，如果dsema信号量的值大于0，该函数所处线程就继续执行下面的语句，并且将信号量的值减1；如果desema的值为0，那么这个函数就阻塞当前线程等待timeout（注意timeout的类型为dispatch_time_t，不能直接传入整形或float型数），如果等待的期间desema的值被dispatch_semaphore_signal函数加1了，且该函数（即dispatch_semaphore_wait）所处线程获得了信号量，那么就继续向下执行并将信号量减1。如果等待期间没有获取到信号量或者信号量的值一直为0，那么等到timeout时，其所处线程自动执行其后语句。</p>
<p>dispatch_semaphore 是信号量，但当信号总量设为 1 时也可以当作锁来。在没有等待情况出现时，它的性能比 pthread_mutex 还要高，但一旦有等待情况出现时，性能就会下降许多。相对于 OSSpinLock 来说，它的优势在于等待时不会消耗 CPU 资源。</p>
<p>如上的代码，如果超时时间overTime设置成&gt;2，可完成同步操作。如果overTime&lt;2的话，在线程1还没有执行完成的情况下，此时超时了，将自动执行下面的代码。</p>
<p>上面代码的执行结果为：</p>
<pre><code>2016-06-29 20:47:52.324 SafeMultiThread[35945:579032] 需要线程同步的操作1 开始
2016-06-29 20:47:55.325 SafeMultiThread[35945:579032] 需要线程同步的操作1 结束
2016-06-29 20:47:55.326 SafeMultiThread[35945:579033] 需要线程同步的操作2
</code></pre><p>如果把超时时间设置为&lt;2s的时候，执行的结果就是：</p>
<pre><code>2016-06-30 18:53:24.049 SafeMultiThread[30834:434334] 需要线程同步的操作1 开始
2016-06-30 18:53:25.554 SafeMultiThread[30834:434332] 需要线程同步的操作2
2016-06-30 18:53:26.054 SafeMultiThread[30834:434334] 需要线程同步的操作1 结束
</code></pre><h4 id="2-3、NSLock"><a href="#2-3、NSLock" class="headerlink" title="2.3、NSLock"></a>2.3、NSLock</h4><pre><code>     NSLock *lock = [[NSLock alloc] init];
     dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//[lock lock];
[lock lockBeforeDate:[NSDate date]];
NSLog(@&quot;需要线程同步的操作1 开始&quot;);
sleep(2);
NSLog(@&quot;需要线程同步的操作1 结束&quot;);
[lock unlock];
  });
 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(1);
if ([lock tryLock]) {//尝试获取锁，如果获取不到返回NO，不会阻塞该线程
    NSLog(@&quot;锁可用的操作&quot;);
    [lock unlock];
}else{
    NSLog(@&quot;锁不可用的操作&quot;);
}
NSDate *date = [[NSDate alloc] initWithTimeIntervalSinceNow:3];
if ([lock lockBeforeDate:date]) {//尝试在未来的3s内获取锁，并阻塞该线程，如果3s内获取不到恢复线程, 返回NO,不会阻塞该线程
    NSLog(@&quot;没有超时，获得锁&quot;);
    [lock unlock];
}else{
    NSLog(@&quot;超时，没有获得锁&quot;);
}
});
</code></pre><p> NSLock是Cocoa提供给我们最基本的锁对象，这也是我们经常所使用的，除lock和unlock方法外，NSLock还提供了tryLock和lockBeforeDate:两个方法，前一个方法会尝试加锁，如果锁不可用(已经被锁住)，刚并不会阻塞线程，并返回NO。lockBeforeDate:方法会在所指定Date之前尝试加锁，如果在指定时间之前都不能加锁，则返回NO。</p>
<p>上面代码的执行结果为：</p>
<pre><code>2016-06-29 20:45:08.864 SafeMultiThread[35911:575795] 需要线程同步的操作1 开始
2016-06-29 20:45:09.869 SafeMultiThread[35911:575781] 锁不可用的操作
2016-06-29 20:45:10.869 SafeMultiThread[35911:575795] 需要线程同步的操作1 结束
2016-06-29 20:45:10.870 SafeMultiThread[35911:575781] 没有超时，获得锁
</code></pre><p>源码定义如下：</p>
<pre><code>  @protocol NSLocking
  - (void)lock;
  - (void)unlock;
  @end
  @interface NSLock : NSObject  {
  @private
  void *_priv;
 }
 - (BOOL)tryLock;
 - (BOOL)lockBeforeDate:(NSDate *)limit;
@property (nullable, copy) NSString *name NS_AVAILABLE(10_5, 2_0);
@end
</code></pre><h4 id="2-4、NSRecursiveLock递归锁"><a href="#2-4、NSRecursiveLock递归锁" class="headerlink" title="2.4、NSRecursiveLock递归锁"></a>2.4、NSRecursiveLock递归锁</h4><pre><code>   //NSLock *lock = [[NSLock alloc] init];
   NSRecursiveLock *lock = [[NSRecursiveLock alloc] init];
   dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  static void (^RecursiveMethod)(int);
  RecursiveMethod = ^(int value) {
  [lock lock];
    if (value &gt; 0) {
           NSLog(@&quot;value = %d&quot;, value);
           sleep(1);
           RecursiveMethod(value - 1);
     }
    [lock unlock];
};
RecursiveMethod(5);
});
</code></pre><p> NSRecursiveLock实际上定义的是一个递归锁，这个锁可以被同一线程多次请求，而不会引起死锁。这主要是用在循环或递归操作中。</p>
<p>这段代码是一个典型的死锁情况。在我们的线程中，RecursiveMethod是递归调用的。所以每次进入这个block时，都会去加一次锁，而从第二次开始，由于锁已经被使用了且没有解锁，所以它需要等待锁被解除，这样就导致了死锁，线程被阻塞住了。调试器中会输出如下信息：</p>
<pre><code>2016-06-30 19:08:06.393 SafeMultiThread[30928:449008] value = 5
2016-06-30 19:08:07.399 SafeMultiThread[30928:449008] -[NSLock lock]: deadlock ( &apos;(null)&apos;)
2016-06-30 19:08:07.399 SafeMultiThread[30928:449008] Break on _NSLockError() to debug.
</code></pre><p>在这种情况下，我们就可以使用NSRecursiveLock。它可以允许同一线程多次加锁，而不会造成死锁。递归锁会跟踪它被lock的次数。每次成功的lock都必须平衡调用unlock操作。只有所有达到这种平衡，锁最后才能被释放，以供其它线程使用。</p>
<p>如果我们将NSLock代替为NSRecursiveLock，上面代码则会正确执行。</p>
<pre><code>2016-06-30 19:09:41.414 SafeMultiThread[30949:450684] value = 5
2016-06-30 19:09:42.418 SafeMultiThread[30949:450684] value = 4
2016-06-30 19:09:43.419 SafeMultiThread[30949:450684] value = 3
2016-06-30 19:09:44.424 SafeMultiThread[30949:450684] value = 2
2016-06-30 19:09:45.426 SafeMultiThread[30949:450684] value = 1
</code></pre><p>如果需要其他功能，源码定义如下：</p>
<pre><code> @interface NSRecursiveLock : NSObject  {
 @private
 void *_priv;
}
- (BOOL)tryLock;
- (BOOL)lockBeforeDate:(NSDate *)limit;
@property (nullable, copy) NSString *name NS_AVAILABLE(10_5, 2_0);
@end
</code></pre><h4 id="2-5、NSConditionLock条件锁"><a href="#2-5、NSConditionLock条件锁" class="headerlink" title="2.5、NSConditionLock条件锁"></a>2.5、NSConditionLock条件锁</h4><pre><code>   NSMutableArray *products = [NSMutableArray array];
   NSInteger HAS_DATA = 1;
   NSInteger NO_DATA = 0;
  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
while (1) {
    [lock lockWhenCondition:NO_DATA];
    [products addObject:[[NSObject alloc] init]];
    NSLog(@&quot;produce a product,总量:%zi&quot;,products.count);
    [lock unlockWithCondition:HAS_DATA];
    sleep(1);
}
 });
   dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
while (1) {
   NSLog(@&quot;wait for product&quot;);
    [lock lockWhenCondition:HAS_DATA];
   [products removeObjectAtIndex:0];
   NSLog(@&quot;custome a product&quot;);
   [lock unlockWithCondition:NO_DATA];
}
  });
</code></pre><p>当我们在使用多线程的时候，有时一把只会lock和unlock的锁未必就能完全满足我们的使用。因为普通的锁只能关心锁与不锁，而不在乎用什么钥匙才能开锁，而我们在处理资源共享的时候，多数情况是只有满足一定条件的情况下才能打开这把锁：</p>
<p>在线程1中的加锁使用了lock，所以是不需要条件的，所以顺利的就锁住了，但在unlock的使用了一个整型的条件，它可以开启其它线程中正在等待这把钥匙的临界地，而线程2则需要一把被标识为2的钥匙，所以当线程1循环到最后一次的时候，才最终打开了线程2中的阻塞。但即便如此，NSConditionLock也跟其它的锁一样，是需要lock与unlock对应的，只是lock,lockWhenCondition:与unlock，unlockWithCondition:是可以随意组合的，当然这是与你的需求相关的。</p>
<p>上面代码执行结果如下：</p>
<pre><code>2016-06-30 20:31:58.699 SafeMultiThread[31282:521698] wait for product
2016-06-30 20:31:58.699 SafeMultiThread[31282:521708] produce a product,总量:1
2016-06-30 20:31:58.700 SafeMultiThread[31282:521698] custome a product
2016-06-30 20:31:58.700 SafeMultiThread[31282:521698] wait for product
2016-06-30 20:31:59.705 SafeMultiThread[31282:521708] produce a product,总量:1
2016-06-30 20:31:59.706 SafeMultiThread[31282:521698] custome a product
2016-06-30 20:31:59.706 SafeMultiThread[31282:521698] wait for product
2016-06-30 20:32:00.707 SafeMultiThread[31282:521708] produce a product,总量:1
2016-06-30 20:32:00.708 SafeMultiThread[31282:521698] custome a product
</code></pre><p>如果你需要其他功能，源码定义如下：</p>
<pre><code>  @interface NSConditionLock : NSObject  {
  @private
  void *_priv;
  }
  - (instancetype)initWithCondition:(NSInteger)condition NS_DESIGNATED_INITIALIZER;
 @property (readonly) NSInteger condition;
- (void)lockWhenCondition:(NSInteger)condition;
- (BOOL)tryLock;
- (BOOL)tryLockWhenCondition:(NSInteger)condition;
- (void)unlockWithCondition:(NSInteger)condition;
- (BOOL)lockBeforeDate:(NSDate *)limit;
- (BOOL)lockWhenCondition:(NSInteger)condition beforeDate:(NSDate *)limit;
@property (nullable, copy) NSString *name NS_AVAILABLE(10_5, 2_0);
@end
</code></pre><h4 id="2-6、NSCondition"><a href="#2-6、NSCondition" class="headerlink" title="2.6、NSCondition"></a>2.6、NSCondition</h4><pre><code>  NSCondition *condition = [[NSCondition alloc] init];
  NSMutableArray *products = [NSMutableArray array];
  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    while (1) {
        [condition lock];
        if ([products count] == 0) {
            NSLog(@&quot;wait for product&quot;);
            [condition wait];
        }
        [products removeObjectAtIndex:0];
        NSLog(@&quot;custome a product&quot;);
        [condition unlock];
    }
});
  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    while (1) {
        [condition lock];
        [products addObject:[[NSObject alloc] init]];
        NSLog(@&quot;produce a product,总量:%zi&quot;,products.count);
        [condition signal];
        [condition unlock];
        sleep(1);
    }
   });
</code></pre><p>一种最基本的条件锁。手动控制线程wait和signal。</p>
<p>[condition lock];一般用于多线程同时访问、修改同一个数据源，保证在同一时间内数据源只被访问、修改一次，其他线程的命令需要在lock 外等待，只到unlock ，才可访问</p>
<p>[condition unlock];与lock 同时使用</p>
<p>[condition wait];让当前线程处于等待状态</p>
<p>[condition signal];CPU发信号告诉线程不用在等待，可以继续执行</p>
<p>上面代码执行结果如下：</p>
<pre><code>2016-06-30 20:21:25.295 SafeMultiThread[31256:513991] wait for product
2016-06-30 20:21:25.296 SafeMultiThread[31256:513994] produce a product,总量:1
2016-06-30 20:21:25.296 SafeMultiThread[31256:513991] custome a product
2016-06-30 20:21:25.297 SafeMultiThread[31256:513991] wait for product
2016-06-30 20:21:26.302 SafeMultiThread[31256:513994] produce a product,总量:1
2016-06-30 20:21:26.302 SafeMultiThread[31256:513991] custome a product
2016-06-30 20:21:26.302 SafeMultiThread[31256:513991] wait for product
2016-06-30 20:21:27.307 SafeMultiThread[31256:513994] produce a product,总量:1
2016-06-30 20:21:27.308 SafeMultiThread[31256:513991] custome a product
</code></pre><h4 id="2-7、pthread-mutex"><a href="#2-7、pthread-mutex" class="headerlink" title="2.7、pthread_mutex"></a>2.7、pthread_mutex</h4><pre><code>__block pthread_mutex_t theLock;
pthread_mutex_init(&amp;theLock, NULL);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        pthread_mutex_lock(&amp;theLock);
        NSLog(@&quot;需要线程同步的操作1 开始&quot;);
        sleep(3);
        NSLog(@&quot;需要线程同步的操作1 结束&quot;);
        pthread_mutex_unlock(&amp;theLock);
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        sleep(1);
        pthread_mutex_lock(&amp;theLock);
        NSLog(@&quot;需要线程同步的操作2&quot;);
        pthread_mutex_unlock(&amp;theLock);
});
</code></pre><p>c语言定义下多线程加锁方式。</p>
<p>1：pthread_mutex_init(pthread_mutex_t mutex,const pthread_mutexattr_t attr);</p>
<p>初始化锁变量mutex。attr为锁属性，NULL值为默认属性。</p>
<p>2：pthread_mutex_lock(pthread_mutex_t mutex);加锁</p>
<p>3：pthread_mutex_tylock(<em>pthread_mutex_t </em>mutex);加锁，但是与2不一样的是当锁已经在使用的时候，返回为EBUSY，而不是挂起等待。</p>
<p>4：pthread_mutex_unlock(pthread_mutex_t *mutex);释放锁</p>
<p>5：pthread_mutex_destroy(pthread_mutex_t* mutex);使用完后释放</p>
<p>代码执行操作结果如下：</p>
<pre><code>2016-06-30 21:13:32.440 SafeMultiThread[31429:548869] 需要线程同步的操作1 开始
2016-06-30 21:13:35.445 SafeMultiThread[31429:548869] 需要线程同步的操作1 结束
2016-06-30 21:13:35.446 SafeMultiThread[31429:548866] 需要线程同步的操作2
</code></pre><h4 id="2-8、pthread-mutex-recursive"><a href="#2-8、pthread-mutex-recursive" class="headerlink" title="2.8、pthread_mutex(recursive)"></a>2.8、pthread_mutex(recursive)</h4><pre><code>__block pthread_mutex_t theLock;
  //pthread_mutex_init(&amp;theLock, NULL);
  pthread_mutexattr_t attr;
  pthread_mutexattr_init(&amp;attr);
  pthread_mutexattr_settype(&amp;attr, PTHREAD_MUTEX_RECURSIVE);
  pthread_mutex_init(&amp;lock, &amp;attr);
  pthread_mutexattr_destroy(&amp;attr);
  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
      static void (^RecursiveMethod)(int);
      RecursiveMethod = ^(int value) {
              pthread_mutex_lock(&amp;theLock);
              if (value &gt; 0) {
                  NSLog(@&quot;value = %d&quot;, value);
                  sleep(1);
                  RecursiveMethod(value - 1);
              }
              pthread_mutex_unlock(&amp;theLock);
       };
      RecursiveMethod(5);
  });
</code></pre><p>这是pthread_mutex为了防止在递归的情况下出现死锁而出现的递归锁。作用和NSRecursiveLock递归锁类似。</p>
<p>如果使用pthread_mutex_init(&amp;theLock, NULL);初始化锁的话，上面的代码会出现死锁现象。如果使用递归锁的形式，则没有问题。</p>
<h4 id="2-9、OSSpinLock"><a href="#2-9、OSSpinLock" class="headerlink" title="2.9、OSSpinLock"></a>2.9、OSSpinLock</h4><pre><code>__block OSSpinLock theLock = OS_SPINLOCK_INIT;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    OSSpinLockLock(&amp;theLock);
    NSLog(@&quot;需要线程同步的操作1 开始&quot;);
    sleep(3);
    NSLog(@&quot;需要线程同步的操作1 结束&quot;);
    OSSpinLockUnlock(&amp;theLock);
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    OSSpinLockLock(&amp;theLock);
    sleep(1);
    NSLog(@&quot;需要线程同步的操作2&quot;);
    OSSpinLockUnlock(&amp;theLock);
});
</code></pre><p>OSSpinLock 自旋锁，性能最高的锁。原理很简单，就是一直 do while 忙等。它的缺点是当等待时会消耗大量 CPU 资源，所以它不适用于较长时间的任务。 不过最近YY大神在自己的博客不再安全的 OSSpinLock中说明了OSSpinLock已经不再安全，请大家谨慎使用。</p>
<h3 id="三、性能对比"><a href="#三、性能对比" class="headerlink" title="三、性能对比"></a>三、性能对比</h3><p><br><br>对以上各个锁进行1000000此的加锁解锁的空操作时间如下：</p>
<pre><code>OSSpinLock: 46.15 ms
dispatch_semaphore: 56.50 ms
pthread_mutex: 178.28 ms
NSCondition: 193.38 ms
NSLock: 175.02 ms
pthread_mutex(recursive): 172.56 ms
NSRecursiveLock: 157.44 ms
NSConditionLock: 490.04 ms
@synchronized: 371.17 ms
</code></pre><p>总的来说：</p>
<p>OSSpinLock和dispatch_semaphore的效率远远高于其他。</p>
<p>@synchronized和NSConditionLock效率较差。</p>
<p>鉴于OSSpinLock的不安全，所以我们在开发中如果考虑性能的话，建议使用dispatch_semaphore。</p>
<p>如果不考虑性能，只是图个方便的话，那就使用@synchronized。</p>

      
    </div>
    
  </div>
  
    
<nav id="article-nav">
  
    <a href="/2016/05/08/Swift 项目/" id="article-nav-newer" class="article-nav-link-wrap">
      <strong class="article-nav-caption"><</strong>
      <div class="article-nav-title">
        
          Swift 项目
        
      </div>
    </a>
  
  
    <a href="/2016/05/05/开源APP项目/" id="article-nav-older" class="article-nav-link-wrap">
      <div class="article-nav-title">开源APP项目</div>
      <strong class="article-nav-caption">></strong>
    </a>
  
</nav>

  
</article>


<div class="share_jia">
	<!-- JiaThis Button BEGIN -->
	<div class="jiathis_style">
		<span class="jiathis_txt">Share to: &nbsp; </span>
		<a class="jiathis_button_facebook"></a> 
    <a class="jiathis_button_twitter"></a>
    <a class="jiathis_button_plus"></a> 
    <a class="jiathis_button_tsina"></a>
		<a class="jiathis_button_cqq"></a>
		<a class="jiathis_button_douban"></a>
		<a class="jiathis_button_weixin"></a>
		<a class="jiathis_button_tumblr"></a>
    <a href="http://www.jiathis.com/share" class="jiathis jiathis_txt jtico jtico_jiathis" target="_blank"></a>
	</div>
	<script type="text/javascript" src="http://v3.jiathis.com/code/jia.js?uid=1405949716054953" charset="utf-8"></script>
	<!-- JiaThis Button END -->
</div>






<div class="duoshuo">
	<!-- 多说评论框 start -->
	<div class="ds-thread" data-thread-key="iOS中保证线程安全的几种方式与性能对比" data-title="iOS中保证线程安全的几种方式与性能对比" data-url="http://yoursite.com/2016/05/07/iOS中保证线程安全的几种方式与性能对比/"></div>
	<!-- 多说评论框 end -->
	<!-- 多说公共JS代码 start (一个网页只需插入一次) -->
	<script type="text/javascript">
	var duoshuoQuery = {short_name:"true"};
	(function() {
		var ds = document.createElement('script');
		ds.type = 'text/javascript';ds.async = true;
		ds.src = (document.location.protocol == 'https:' ? 'https:' : 'http:') + '//static.duoshuo.com/embed.js';
		ds.charset = 'UTF-8';
		(document.getElementsByTagName('head')[0] 
		 || document.getElementsByTagName('body')[0]).appendChild(ds);
	})();
	</script>
	<!-- 多说公共JS代码 end -->
</div>




</div>
      <footer id="footer">
  <div class="outer">
    <div id="footer-info">
    	<div class="footer-left">
    		&copy; 2019 Grx
    	</div>
      	<div class="footer-right">
      		<a href="http://hexo.io/" target="_blank">Hexo</a>  Theme <a href="https://github.com/litten/hexo-theme-yilia" target="_blank">Yilia</a> by Litten
      	</div>
    </div>
  </div>
</footer>
    </div>
    
  <link rel="stylesheet" href="/fancybox/jquery.fancybox.css">


<script>
	var yiliaConfig = {
		fancybox: true,
		mathjax: true,
		animate: true,
		isHome: false,
		isPost: true,
		isArchive: false,
		isTag: false,
		isCategory: false,
		open_in_new: false
	}
</script>
<script src="http://7.url.cn/edu/jslib/comb/require-2.1.6,jquery-1.9.1.min.js"></script>
<script src="/js/main.js"></script>






<script type="text/x-mathjax-config">
MathJax.Hub.Config({
    tex2jax: {
        inlineMath: [ ['$','$'], ["\\(","\\)"]  ],
        processEscapes: true,
        skipTags: ['script', 'noscript', 'style', 'textarea', 'pre', 'code']
    }
});

MathJax.Hub.Queue(function() {
    var all = MathJax.Hub.getAllJax(), i;
    for(i=0; i < all.length; i += 1) {
        all[i].SourceElement().parentNode.className += ' has-jax';                 
    }       
});
</script>

<script type="text/javascript" src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML">
</script>


  </div>
</body>
</html>